<?php
################################
##### PHPS Pastebin by edK #####
################################
#Version: 0.25
#Rev 25.01.2010
#Please report any bugs 2 The /*47*/ MemoryOfTre /*d0t*/ es
#My nicks @ irc.freenode.net(ORDER BY `total_time_used` DESC): _AnywhereIs_, InCaribbeanBlue, Web31337
#More: http://phps.web31337.org/info/
#
#Hosted @ google code: http://phpspastebin.googlecode.com/
#Hosted @ sourceforge: http://sf.net/projects/phpspastebin/

# Great thanx to many irc.freenode.net##php users & ops ;)
# <3 phr33n0d3!
# Special thanx to InAjaxer a-ka T1AM0

# Why use PHPS Pastebin?
# * No SQL, no any other server extentions required to run it!
# * Has no vulnerabilities! (Or has if you discoved one in PHP functions used here or zlib)
# * No client capabilities required except for HTML rendering! No cookies, no javascript, no external plugins binding!
# * Very light-all in one script!

# How to install:
# 1. Set a file creating permission for webserver to a sources directory.
# 2. Copy this file to root directory or any other directory where you want pastebin script to be accessible from.
# 3. Set a paste index file secret location(DO NOT EXPOSE IT OR USE REGULAR EASY LOCATIONS FOR INDEX FILE!)
#    While actually there's nothing could be easily cracked there, it stores md5 of code(also md5-presented), required to remove paste. Knowing that code allows to remove single paste.
# ~ 4. (OPTIONAL) Tune up settings inside script. You can also use "valid html" image (html.png, taken from validator.w3.org) from the package on the pages.
# 4. Enjoy!

# License: ./LICENSE
#
##########################################################################
## Copyright (C) 2009-2010 edK											##
## This program is free software: you can redistribute it and/or modify ##
## it under the terms of the GNU General Public License as published by ##
## the Free Software Foundation, either version 3 of the License, or	##
## (at your option) any later version.									##
##																		##
## This program is distributed in the hope that it will be useful,		##
## but WITHOUT ANY WARRANTY; without even the implied warranty of		##
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the		##
## GNU General Public License for more details.							##
##																		##
## You should have received a copy of the GNU General Public License	##
## along with this program. If not, see <http://www.gnu.org/licenses/>. ##
##########################################################################

if(!file_exists('./conf.php'))
	die('./conf.php is not present, please move your config here or use the one from source package.');

require('./conf.php');

######################################
################ MAIN ################
######################################

	if($debug) {
		//go to maximum debugging level [PHPS Pastebin(since v 0.24) is tested against this level]
		ini_set('display_errors','on'); 
		error_reporting(E_ALL | E_STRICT);
		}
	
	@ini_set("default_charset","utf-8");
	
	//some definitions
	
	$bl="<a href=\"{$_SERVER['SCRIPT_NAME']}\">Go back</a>";//backlink template
	$h=$_SERVER['HTTP_HOST'];
	$sn=$_SERVER['SCRIPT_NAME'];
	$ip=$_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT'];
	
	//pre-filter input
	if(function_exists("get_magic_quotes_gpc") and get_magic_quotes_gpc()) {
		$_POST = array_map('stripslashes_deep', $_POST);
		}
	
	//actions

	if(!empty($_POST['txt'])) {
		if(!empty($passwd)) 
			check_passwd($passwd);//check for password, it is set in config
	 
		$fname=generate_name();
		while(file_exists($fname)) 
			$fname=generate_name();//regenerate new, until filename will not exist
	 
		check_length();
		$lvl=check_gzlevel($gzlvl);
		$data=print_loader(htmlspecialchars($_POST['title']),$sources_title,1,$lvl);
		if(!$lvl) 
			$data.=$_POST['txt'];#compression disabled or not supported by webserver
		else 
			$data.=gzcompress($_POST['txt'],$lvl);
		$f=@fopen($fname,'w');//make new file 
		if(!is_resource($f)) 
			die("Cannot create a file. Check if webserver has rights to create new files in home directory.<br>$bl.");
		$r=@fwrite($f,$data);
		if($r!=strlen($data))
			die("Cannot write to open file. Something is completely wrong on this server. You can still $bl to try again.");
		@fclose($f);
	 
		//Prepare view and remove URLs
		if($fname[0]=='.') 
			$uri=substr($fname,1);//replace ./ with / in URL. ./ is taken from global $path, from conf.php.
		$fp=maybe_addslash(dirname($sn));//get the path from URI
		//Now add path to actual path in URL.
		//This is a bugfix.
		//Example of bug: if you use THIS file in /somedir/, when you paste source, you will get $fname1="./somefile.php", and after, /somefile.php, but remind that $path is a relative path of THIS script but no a server root. So it will result in linking to /somefile.php while it meant to be /somedir/somefile.php.
		$uri=substr($fp,0,-1).$uri;//kill slash from URI dirname and add actual file name after it.
		$url="http://".$h.$uri;//finally, create paste view url
		if(!empty($index)) {
			//prepare data for adding to index.
			$rm_key=sha1(generate_garbage(mt_rand(32,64),1).$fname);//some unpredictable random key to remove paste
			$rm_url="http://".$h.$sn."?rm=$rm_key";
			$rm_url='<br>You can remove your post using the following link(save it if you are planning to remove this post!): <a href="'.$rm_url.'">'.$rm_url.'</a>';//paste remove note with an URL
			$private=(!empty($_POST['private']) && $_POST['private']=='on') ? 1 : 0;//private paste?
			//Add this paste into index.
			$st=(!empty($_POST['title'])) ? htmlspecialchars($_POST['title']) : 'Untitled';
			$data=serialize(array(
				$fname,			/* Actual filename in order to remove it */
				$uri,			/* URI to create link */
				$st,			/* Paste title */
				sha1($rm_key),	/* Hash of remove key, hashed in order to protect pastes from being removed by just reading index file. */
				time(),
				$ip,			/* Paster IP:Port */
				$private		/* Private paste? */
				));
			$f=@fopen($index,'a');
			if(!is_resource($f))
				die('Cannot open index file for writing. Failed to add post to index.<br>'.$bl);
			$r=@fwrite($f,$data."\n");
			if($r!=strlen($data)+1)
				die('Cannot correctly add new entry to index file: error while writing file.<br>'.$bl);
			@fclose($f);
		    
			die('Your code is available at <a href="'.$url.'">'.$url.'</a>.'.$rm_url.'<br><a href="'.$sn.'">Have more source to add?</a>');
			}
		}

	if(!empty($_GET['rm'])) {
		//remove paste request.
		//look through the index to find out if there was post with this key
		if(!empty($index)) {
			//index file is set. Using index system
			if(!file_exists($index)) die("Unable to locate the index file. Check your config!<br><a href=\"{$_SERVER['SCRIPT_NAME']}\">Go back</a>");
			$list=file($index);
			$cnt=sizeof($list)-1;
			for($i=0;$i<=$cnt;$i++) {
				$a=unserialize($list[$i]);
				if($a[3]==sha1($_GET['rm'])) {
					//found it! remove the file and index entry
					if(!@unlink($a[0])) 
						die("Failed to remove the file!<br>$bl");//removing of actual file failed
					unset($list[$i]);//remove entry from index
					//remove empty lines
					$data=implode($list);
					$f=@fopen($index,'w');
					if(!is_resource($f))
						die('Cannot open index file for overwriting. Failed to rebuild index file.<br>'.$bl);
					$r=@fwrite($f,$data);
					if($r!=strlen($data))
						die('Cannot correctly update index file: error while writing file.<br>'.$bl);
					@fclose($f);
					die("Your entry titled \"{$a[2]}\" located at \"{$a[1]}\" successfully removed!<br><a href=\"{$_SERVER['SCRIPT_NAME']}\">Go back</a>");
					}
				}
			die("The key is invalid or the post already removed.<br>$bl");
			}
		//no index defined, ignore request.
		}

#####################################
############# FUNCTIONS #############
#####################################

//general stuff
 
	function maybe_addslash($p) {
		if($p[strlen($p)-1]!='/')$p.="/";
		return $p;
		}

	function stripslashes_deep($value) {
		$value = is_array($value) ?
		array_map('stripslashes_deep', $value) :
		stripslashes($value);
		return $value;
		}
	
	function generate_garbage($sz,$base64it=0,$dev_urandom=0) {
		//binary garbage generator.
		//to explicitly use unix-way (/dev/urandom) set $dev_urandom.
		$garbage="";
		if($dev_urandom or @is_file("/dev/urandom")) {
			$f=@fopen("/dev/urandom",'r');
			if(!is_resource($f))
				return 0;
			$garbage=@fread($f,$sz);
			@fclose($f);
			return (($base64it) ? base64_encode($garbage) : $garbage);
		}
		
		for($i=0;$i<$sz;$i++) {
				//ugly mess for windoze, and other torture-tools
				$garbage.=chr(mt_rand(0,255));
			}
		return (($base64it) ? base64_encode($garbage) : $garbage);
}	

	function truncstr($str,$cnt) {
		//simple string truncate :)
		if(iconv_strlen($str,"utf-8")>$cnt) 
			return iconv_substr($str,0,$cnt-3,"utf-8")."...";
		return $str;
		}
		
//system stuff

	function check_length() {
		global $limit_source,$limit_title,$bl;
		//check if limits are set and if source/title is longer than limit
		if($limit_source and (!empty($_POST['txt'])) and iconv_strlen($_POST['txt'],"utf-8")>$limit_source) 
			die("Too long source! You can add max $limit_source-characters long code.<br>$bl");
		if($limit_title and (!empty($_POST['title'])) and iconv_strlen($_POST['title'],"utf-8")>$limit_title) 
			die("Too long title! You can set max $limit_title-characters long title.<br>$bl");
		}

	function check_gzlevel($lvl) {
		//check gzip compression level and set valid value
		if(!function_exists("gzcompress") or !$lvl) 
			return 0;
		if($lvl<0 or $lvl>9) return 5;#default
		return $lvl;
		}

	function check_passwd($passwd) {
		global $bl;
		if(empty($_POST['pass'])) 
			die("No password! Only site admin can add sources here when entering valid password.<br>$bl.");
		if($_POST['pass']!=$passwd) 
			die("Password incorrect!<br>$bl.");
		}

	function generate_name() {
		//generates random filename
		global $path,$maxlen,$ext;
		if(!isset($ext)) 
			$ext=".php";
		if($maxlen<8 or $maxlen>=40) 
			$maxlen=8;
		return $path.substr(md5(generate_garbage(40,1)),0,8+mt_rand(0,$maxlen-8)).$ext;
	}
	
//HTML generators

	function print_index($return=0) {
		//prints HTML table and recent pastes index in it
		global $index,$index_js,$index_show_time,$index_show_ip,$index_width,$index_limit;
		$output=(($index_js) ? "\n<script language=\"javascript\" type=\"text/javascript\">function viewpaste(pasteurl){document.location=pasteurl;}</script>\n" : "")."<table>\n<tr>\n<td valign=\"top\" width=\"$index_width\">\n<b>Recent Entries</b><br><br>\n\n<table>\n";
		//index file existance has been checked before this function call
		$idx=@file($index);
		if(empty($idx))
			return 0;//index is empty, nothing to return. Actually that is checked before calling this function in HTML template.
		$idx=array_reverse($idx);//most recent entries are last!
		$c=sizeof($idx);
		for($i=0;$i<$c;$i++) {
			if($i==$index_limit) break;
			$entry=unserialize($idx[$i]);
			if(!empty($entry[6])) continue;//this should not be listed, marked as private paste
			$time=($index_show_time) ? '<br><span class=small>At '.gmdate("d.m.y H:i:s",$entry[4]).' GMT</span>' : '';
			$ip=($index_show_ip) ? '<br><span class=small>['.$entry[5].']</span>' : '';
			$output.=" <tr><td class=c2".(($index_js) ? " onclick=\"viewpaste('{$entry[1]}')\"" : '').">-&nbsp;<a href=\"{$entry[1]}\" style=\"font-weight:bold;\">".truncstr($entry[2],30)."</a>".$time.$ip."</td></tr>\n";
			}
		$output.="</table>\n\n</td>\n<td valign=\"top\">\n";
		if($return) 
			return $output;
		echo $output;
		return 1;
	}

	function print_loader($title="",$sources_title="PHPS Pastebin - Source Review",$return=1,$gz=1)
	{#get loader source
		$img=maybe_addslash(dirname($_SERVER['SCRIPT_NAME']))."html.png";#image should be stored in the path of this script
		$src=<<<LDR_SRC
<?php
################################
##### PHPS Pastebin by edK #####
################################

#This file is a print code for pasted PHP source.
#WARNING: This is a generated file! Do not edit! It may fail to parse after editing with standard tools like windows's notepad.
#The source pasted is gzipped and is placed just after __halt_compiler();

if(isset(\$_GET['source'])) die(@highlight_file(__FILE__,1));#show source

\$title="{$title}";
\$gz={$gz};#is source gzipped?
\$image_path="{$img}";
\$addsource="{$_SERVER['SCRIPT_NAME']}";
\$endhtml='<br><a href="'.htmlspecialchars(\$_SERVER['SCRIPT_NAME'].'?source').'">[View source of this file]</a> <a href="'.\$addsource.'">[Paste new source]</a><br>This site is powered by <a href="http://phps.web31337.org/info/">PHPS Pastebin</a> coded by edK<br><br><a href="http://validator.w3.org/check?uri=referer"><img src="'.\$image_path.'" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a></p></body></html>';

\$output='<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>'; 
 #set the title
 if(!empty(\$title)) \$output.=\$title; 
 else \$output.="{$sources_title}";
\$output.='</title>
</head>
<body>';

\$thecode=file_get_contents(__FILE__);#get THIS FILE
\$thecode=substr(\$thecode,strpos(\$thecode,'__halt_compiler();'.'?>')+20,strlen(\$thecode));
if((\$gz) and (function_exists("gzuncompress"))) 
 {#compression available, try to unpack source
 \$thecode=gzuncompress(\$thecode);
 if(\$thecode===FALSE) die(\$output.'<p align="center">Cannot unpack source-something is wrong with zlib or packed data is not well-formatted.'.\$endhtml);
 }
if((\$gz) and (!function_exists("gzuncompress"))) die(\$output.'<p align="center">Your webserver has no zlib required to unpack the data in script.'.\$endhtml);
\$output.=highlight_string(\$thecode,1);
\$output.='<p align="center"><a href="'.htmlspecialchars(\$_SERVER['SCRIPT_NAME'].'?source').'">[View source of this file]</a> <a href="'.\$addsource.'">[Paste new source]</a><br>This site is powered by <a href="http://phps.web31337.org/info/">PHPS Pastebin</a> coded by edK<br><br><a href="http://validator.w3.org/check?uri=referer"><img src="'.\$image_path.'" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a></p></body></html>';
print \$output;
__halt_compiler();?>
LDR_SRC;
if(!$return) 
	echo $src;
else 
	return $src;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title><?php echo (empty($title)) ? "PHPS Pastebin" : $title;?></title>
<style type="text/css">
body{font-family: Verdana;}
.c1{border: 1px solid #CCCCCC; color: #cccccc; background-color: #202020;}
.c2{background-color: #202020;}
.c2:hover{background-color: #404040;}
.small{color:green;font-family:'Courier New';font-size:10pt;}
</style>
</head>
<body bgcolor="#202020" text="#CCCCCC" alink="#909090" vlink="#909090" link="#909090"><?php if($index_show && file_exists($index) && filesize($index)) $pr=print_index();  ?>
<form action="<?php echo htmlspecialchars($_SERVER['REQUEST_URI']);?>" method="POST">
Title: <input class=c1 style="width: 600px" name="title"><br><br>
<?php if(!empty($passwd)) echo 'Admin password: <input class=c1 type="password" name="pass"><br><br>'."\n"; ?>Your PHP source:<br><textarea class=c1 rows=30 cols=95 name="txt"></textarea><br><br>
<label><input type="checkbox" name="private">&nbsp;Make this paste private(do not show in Recent Entries)</label><br><br>
<input type="submit" style="width: 180px; height: 50px; font-family: Verdana; font-size: 28pt;" value="Paste!"><?php if($index_show_ip and $index_show) echo '<p style="width:800px;border:1px solid red;background-color:#201616;font-weight:bold;">&nbsp;<br>&nbsp;Warning: Site administrator enabled option that your IP is logged and publicy visible at the pastes index!<br>&nbsp;</p>'; ?>
</form><?php if(!empty($pr)) echo "\n</td></tr></table>\n";?>
<p align=center>This site is powered by <a href="http://phps.web31337.org/info/" style="font-weight:bold;">PHPS Pastebin</a><br>This site has valid HTML 4.01!</p>
<p align=center><a href="http://validator.w3.org/check?uri=referer"><img src="html.png" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a></p>
</body>
</html>
